package cn.openea.perpartner.meily.web.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.openea.perpartner.common.core.exception.ServiceException;
import cn.openea.perpartner.common.core.utils.DateUtils;
import cn.openea.perpartner.common.core.utils.IdWorker;
import cn.openea.perpartner.common.core.utils.StringUtils;
import cn.openea.perpartner.common.core.web.domain.AjaxResult;
import cn.openea.perpartner.meily.util.CommonUtils;
import cn.openea.perpartner.meily.web.entity.Goods;
import cn.openea.perpartner.meily.web.entity.GroupBuying;
import cn.openea.perpartner.meily.web.entity.GroupBuyingSub;
import cn.openea.perpartner.meily.web.entity.Provider;
import cn.openea.perpartner.meily.web.entity.dto.GroupBuyingDTOFactory;
import cn.openea.perpartner.meily.web.entity.dto.GroupBuyingDTOFactory.*;
import cn.openea.perpartner.meily.web.entity.dto.GroupDto;
import cn.openea.perpartner.meily.web.mapper.GroupBuyingMapper;
import cn.openea.perpartner.meily.web.service.IGoodsService;
import cn.openea.perpartner.meily.web.service.IGroupBuyingService;
import cn.openea.perpartner.meily.web.service.IGroupBuyingSubService;
import cn.openea.perpartner.meily.web.service.IProviderService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * (GroupBuying)表服务实现类
 *
 * @author rains
 * @since 2023-02-07 10:12:15
 */
@Slf4j
@Service("groupBuyingService")
public class GroupBuyingServiceImpl extends ServiceImpl<GroupBuyingMapper, GroupBuying> implements IGroupBuyingService {

    @Value("${bpm.rest.url}")
    private String bpmUrl;
    @Value("${meily.header.sysid}")
    private String sysId;
    @Value("${meily.header.syspwd}")
    private String sysPwd;
    @Value("${bpm.msg_appid}")
    private String msgAppid;
    @Value("${bpm.msg_wf_num}")
    private String msgWfNum;
    @Value("${payment.order.authSalt}")
    private String authSalt;
    @Value("${payment.order.getTokenUrl}")
    private String getOrderSysTokenUrl;
    @Value("${payment.order.subPayToWallet}")
    private String subPayToWallet;
    @Value("${integral_sysytem_id}")
    private String INTEGRAL_SYSTEM_ID;

    @Resource
    private IGoodsService goodsService;
    @Autowired
    private IProviderService providerService;
    @Autowired
    private IGroupBuyingSubService groupBuyingSubService;


    private IdWorker worker = new IdWorker(1, 1, 1);

    /**
     * 发布团购
     *
     * @param groupBuying 实例对象
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public AjaxResult publish(GroupBuying groupBuying) throws ParseException {

        if (StrUtil.isBlank(groupBuying.getInitiatorId())) {
            return AjaxResult.error("获取不到当前用户的userid，请检查是否登录！");
        }
        if (StrUtil.isBlank(groupBuying.getInitiatorName())) {
            return AjaxResult.error("获取不到当前用户的userName，请检查是否登录！");
        }

        // 1.获取商品信息
        Goods goods = goodsService.queryDetails(groupBuying.getGoodsNo(), groupBuying.getGoodsId());

        // 2.团购标题 = 商品名称（编号：xxx[随机3位]）
        String Prefix = CommonUtils.getPrefix(3);
        if (StrUtil.isNotBlank(goods.getGoodsName()) && StrUtil.isNotBlank(Prefix)) {
            groupBuying.setTitle(goods.getGoodsName() + "(" + Prefix + ")");
        }

        // 3.根据发布时间与帮卖周期计算结束时间
        String helpCycle = goods.getHelpCycle();// 帮卖周期(天)
        String releaseTime = goods.getReleaseTime();// 上架时间

        if (StrUtil.isBlank(releaseTime)) {
            return AjaxResult.error("获取不到商品上架时间，无法发布拼团！");
        }
        if (StrUtil.isBlank(helpCycle) || "0".equals(helpCycle)) {
            return AjaxResult.error("获取不到商品团购周期，无法发布拼团！");
        }

        String today = DateUtil.now();// 开始时间
        int diff = Math.abs(DateUtils.daysBetween(releaseTime, today));// 取绝对值

        // 上架时间距离今天的天数 / 帮卖周期 向上取整，便获取到需要增加的周期倍数
        int multiple = (int) Math.ceil(diff / Double.parseDouble(helpCycle));

        // 结束时间 = 上架时间 + (帮卖周期 * 需要增加的周期倍数)
        String endTime = DateUtils.plusDay(Integer.parseInt(helpCycle) * multiple, releaseTime);

        // 4.获取周期串
        String cycle = goodsService.cycle(goods.getGoodsId());

        // 5.封装团购对象
        groupBuying.setGoodsId(goods.getGoodsId().toString());// 商品记录唯一ID
        groupBuying.setGoodsName(goods.getGoodsName());// 商品名称
        groupBuying.setGoodsImages(goods.getGoodsImages());// 商品图
        groupBuying.setOriginalPrice(goods.getGoodsPrice());// 商品原价
        groupBuying.setIndustry("营销宣传");// 如果行业字段为空，则设置默认值
        groupBuying.setKeyWord(goods.getGoodsTag());// 商品标签
        groupBuying.setUnitPrice(goods.getDiscountPrice());// 单价z
        groupBuying.setSceneAssetsId(goods.getSceneAssetsId());// 定制ID
        groupBuying.setSceneId(goods.getSceneId());// 场景ID
        groupBuying.setStartTime(today);// 开始时间
        groupBuying.setEndTime(endTime);// 结束时间
        groupBuying.setTaskNumber(String.valueOf(worker.nextId()));// 任务单号，全使用数字
        groupBuying.setDistributionReward(goods.getForemanReward());// 线上服务商分销报酬（原团长帮卖报酬）
        groupBuying.setCycle(cycle);// 周期串
        BigDecimal getSupplyPrice = BigDecimal.ZERO;
        if (StringUtils.isNotBlank(goods.getSupplyPrice())) {
            getSupplyPrice = new BigDecimal(goods.getSupplyPrice().replaceAll("\\s", ""));
        }
        groupBuying.setSupplyPrice(getSupplyPrice);// 供货价
        groupBuying.setShippingLink(goods.getShippingLink());// 发货链接
        groupBuying.setGoodsBrand(goods.getBrand());// 所属品牌
        groupBuying.setGoodsType(goods.getGoodsType());// 商品类型

        // 商品总量，默认100
        if (StrUtil.isBlank(groupBuying.getTotalQuantity())) {
            groupBuying.setTotalQuantity("100");
        }

        // 剩余数量，默认100
        if (StrUtil.isBlank(groupBuying.getRemainQuantity())) {
            groupBuying.setRemainQuantity("100");
        }

        // 每人参与次数。0:不限；1:仅限一次；默认0
        if (StrUtil.isBlank(groupBuying.getJoinNum())) {
            groupBuying.setJoinNum("0");
        }

        // 已团购的商品数量。默认0
        if (StrUtil.isBlank(groupBuying.getUsedQuantity())) {
            groupBuying.setUsedQuantity("0");
        }

        // 参与团购的人数。默认0
        if (StrUtil.isBlank(groupBuying.getCustomerNum())) {
            groupBuying.setCustomerNum("0");
        }

        // 状态。0:废弃；1:启用；2:结束；默认1
        if (StrUtil.isBlank(groupBuying.getStatus())) {
            groupBuying.setStatus("1");
        }

        /**
         * 第2步 发布团购（保存团购数据）
         */
        if (!insert(groupBuying)) {
            throw new ServiceException("发布失败！");
        }

        /**
         * 第3步 更新服务商信息
         */
        LambdaQueryWrapper<Provider> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Provider::getUserId, groupBuying.getInitiatorId());
        wrapper.ne(Provider::getIsInitiateGroup, 1);
        Provider provider = providerService.getOne(wrapper);
        if(BeanUtil.isNotEmpty(provider)){
            // 更新数据
            LambdaUpdateWrapper<Provider> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(Provider::getUserId, provider.getUserId());
            updateWrapper.set(Provider::getIsInitiateGroup, 1);
            if(!providerService.update(updateWrapper)){
                throw new ServiceException("更新服务商信息出错了！");
            }
        }
        return AjaxResult.success("发布成功！", groupBuying);
    }

    /**
     * 查询团购详情
     *
     * @param id
     * @return 实例对象
     */
    @Override
    public AjaxResult queryById(String id) {

        if (StrUtil.isBlank(id)) {
            return AjaxResult.error("获取不到团购记录唯一id！");
        }
        LambdaQueryWrapper<GroupBuying> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(GroupBuying::getId, id);

        GroupBuying groupBuying = getOne(wrapper, false);
        if (BeanUtil.isEmpty(groupBuying)) {
            return AjaxResult.error("根据文档id(" + id + ")，获取不到团购详情！");
        }
        return AjaxResult.success("获取团购详情成功！", groupBuying);
    }

    /**
     * 获取正在进行中的拼团列表
     *
     * @param groupDto 实例对象
     * @return 实例对象
     */
    @Override
    public AjaxResult getSimilarGroups(GroupDto groupDto) {

        if (StrUtil.isBlank(groupDto.getInitiatorId())) {
            return AjaxResult.error("获取不到当前用户的userid，请检查是否登录！");
        }

        if (StrUtil.isBlank(groupDto.getCycle())) {
            return AjaxResult.error("获取不到cycle，请检查参数是否有误！");
        }

        if (StrUtil.isBlank(groupDto.getGoodsId())) {
            return AjaxResult.error("获取不到goodsId，请检查参数是否有误！");
        }

        if (StrUtil.isBlank(groupDto.getGoodsNo())) {
            return AjaxResult.error("获取不到goodsNo，请检查参数是否有误！");
        }

        LambdaQueryWrapper<GroupBuying> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(GroupBuying::getInitiatorId, groupDto.getInitiatorId());
        wrapper.eq(GroupBuying::getGoodsId, groupDto.getGoodsId());
        wrapper.eq(GroupBuying::getGoodsNo, groupDto.getGoodsNo());
        wrapper.eq(GroupBuying::getCycle, groupDto.getCycle());
        wrapper.eq(GroupBuying::getStatus, "1");// 状态。0:废弃；1:启用；2:结束；
        wrapper.orderByDesc(GroupBuying::getCreateTime);

        return AjaxResult.success("获取正在进行中的拼团列表成功！", list(wrapper));
    }

    /**
     * 获取我发布的团购列表---分页
     *
     * @param groupDto
     * @return
     */
    @Override
    public List<GroupBuying> getPublishGroupPurchaseList(GroupDto groupDto) {

        LambdaQueryWrapper<GroupBuying> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(GroupBuying::getInitiatorId, groupDto.getInitiatorId());// 发起人
        if (StrUtil.isNotBlank(groupDto.getSceneAssetsId())) {
            wrapper.eq(GroupBuying::getSceneAssetsId, groupDto.getSceneAssetsId());// 定制ID
        }
        if (StrUtil.isNotBlank(groupDto.getSceneId())) {
            wrapper.eq(GroupBuying::getSceneId, groupDto.getSceneId());// 场景ID
        }
        if (StrUtil.isNotBlank(groupDto.getStatus())) {
            wrapper.eq(GroupBuying::getStatus, groupDto.getStatus());// 场景ID
        }
        if (StrUtil.isNotBlank(groupDto.getSearchStr())) {
            wrapper.and(wrap -> wrap.like(GroupBuying::getTitle, groupDto.getSearchStr())
                    .or().like(GroupBuying::getKeyWord, groupDto.getSearchStr())
                    .or().like(GroupBuying::getGoodsId, groupDto.getSearchStr())
                    .or().like(GroupBuying::getGoodsNo, groupDto.getSearchStr())
                    .or().like(GroupBuying::getGoodsName, groupDto.getSearchStr()));
        }
        wrapper.orderByDesc(GroupBuying::getCreateTime);

        List<GroupBuying> list = list(wrapper);
        return list;
    }

    /**
     * 获取我参与的团购列表---分页
     *
     * @param groupDto
     * @return
     */
    @Override
    public List<GroupBuying> getJoinGroupPurchaseList(GroupDto groupDto) {
        QueryWrapper<GroupBuying> queryWrapper = new QueryWrapper<GroupBuying>();

        StringBuffer queryStr = new StringBuffer();

        if (StrUtil.isNotBlank(groupDto.getPartnerId())) {
            queryStr.append(" and b.partner_id = '" + groupDto.getPartnerId() + "'");
        } else {
            return null;
        }

        // 定制ID
        if (StrUtil.isNotBlank(groupDto.getSceneAssetsId())) {
            queryStr.append(" and b.scene_assets_id = '" + groupDto.getSceneAssetsId() + "'");
        }

        // 场景ID
        if (StrUtil.isNotBlank(groupDto.getSceneId())) {
            queryStr.append(" and b.scene_id = '" + groupDto.getSceneId() + "'");
        }

        String searchStr = groupDto.getSearchStr();
        if (StrUtil.isNotBlank(searchStr)) {
            queryStr.append("and (");
            queryStr.append("a.title like '%" + searchStr + "%'");
            queryStr.append("or a.goods_name like '%" + searchStr + "%'");
            queryStr.append(")");
        }

        queryWrapper.last(" " + queryStr + " order by b.create_time desc ");

        return baseMapper.getJoinGroupPurchaseList(queryWrapper);
    }

    /**
     * 获取我发布的团购总数
     *
     * @param groupDto
     * @return
     */
    @Override
    public long getPublishGroupPurchaseNum(GroupDto groupDto) {

        LambdaQueryWrapper<GroupBuying> queryWrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotBlank(groupDto.getInitiatorId())) {
            queryWrapper.eq(GroupBuying::getInitiatorId, groupDto.getInitiatorId());
        }
        if (StrUtil.isNotBlank(groupDto.getStatus())) {
            queryWrapper.eq(GroupBuying::getStatus, groupDto.getStatus());
        }
        // 定制ID
        if (StrUtil.isNotBlank(groupDto.getSceneAssetsId())) {
            queryWrapper.eq(GroupBuying::getSceneAssetsId, groupDto.getSceneAssetsId());
        }
        // 场景ID
        if (StrUtil.isNotBlank(groupDto.getSceneId())) {
            queryWrapper.eq(GroupBuying::getSceneId, groupDto.getSceneId());
        }
        return baseMapper.selectCount(queryWrapper);

    }

    /**
     * 获取我参与的团购总数
     *
     * @param groupDto
     * @return
     */
    @Override
    public AjaxResult getJoinGroupPurchaseNum(GroupDto groupDto) {

        QueryWrapper<GroupBuying> queryWrapper = new QueryWrapper<GroupBuying>();

        StringBuffer queryStr = new StringBuffer();

        if (StrUtil.isNotBlank(groupDto.getPartnerId())) {
            queryStr.append(" and b.partner_id = '" + groupDto.getPartnerId() + "'");
        } else {
            return AjaxResult.error("获取不到当前用户的userid，请检查是否登录！");
        }

        // 定制ID
        if (StrUtil.isNotBlank(groupDto.getSceneAssetsId())) {
            queryStr.append(" and b.scene_assets_id = '" + groupDto.getSceneAssetsId() + "'");
        }

        // 场景ID
        if (StrUtil.isNotBlank(groupDto.getSceneId())) {
            queryStr.append(" and b.scene_id = '" + groupDto.getSceneId() + "'");
        }

        queryWrapper.last(" " + queryStr + " ");

        return AjaxResult.success("获取我参与的团购总数成功！", baseMapper.getJoinGroupPurchaseNum(queryWrapper));
    }

    /**
     * 结束团购（只修改状态）
     *
     * @param gb 实例对象
     * @return 实例对象
     */
    @Transactional
    @Override
    public AjaxResult finish(GroupBuying gb) {

        if (StrUtil.isBlank(gb.getId())) {
            return AjaxResult.error("获取不到团购记录唯一id！");
        }

        GroupBuying groupBuying = getById(gb.getId());
        if (groupBuying == null || BeanUtil.isEmpty(groupBuying)) {
            return AjaxResult.error("根据文档id(" + gb.getId() + ")，找不到拼团活动！");
        }

        // 状态。0:废弃；1:启用；2:结束；
        if (StrUtil.isNotBlank(gb.getStatus())) {
            groupBuying.setStatus(gb.getStatus());
        }

        // 团购实际结束时间
        if (StrUtil.isNotBlank(gb.getFinishTime())) {
            groupBuying.setFinishTime(gb.getFinishTime());
        }

        // 更新时间
        groupBuying.setUpdateTime(DateUtil.now());

        if (!updateById(groupBuying)) {
            throw new ServiceException("结束团购失败！");
        }
        return AjaxResult.success("结束团购成功！", groupBuying);
    }

    /**
     * 更新团购信息-后台使用
     *
     * @param gb          实例对象
     * @param BuyQuantity 购买数量
     * @return 实例对象
     */
    @Transactional
    @Override
    public AjaxResult update(GroupBuying gb, String BuyQuantity) {

        if (StrUtil.isBlank(gb.getId())) {
            return AjaxResult.error("获取不到团购记录唯一id！");
        }

        LambdaQueryWrapper<GroupBuying> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(GroupBuying::getId, gb.getId());
        GroupBuying groupBuying = getOne(wrapper);
        if (BeanUtil.isEmpty(groupBuying)) {
            return AjaxResult.error("根据文档id(" + gb.getId() + ")，获取不到团购详情！");
        }

        // 购买数量不为空，更新数据
        if (StrUtil.isNotBlank(BuyQuantity)) {
            // 更新已参与接龙的人数(原来的基础上加1)
            if (StrUtil.isNotBlank(groupBuying.getCustomerNum())) {
                groupBuying.setCustomerNum(Integer.toString(Integer.parseInt(groupBuying.getCustomerNum()) + 1));
            }

            // 更新已团购的商品数量
            if (StrUtil.isNotBlank(groupBuying.getUsedQuantity())) {
                groupBuying.setUsedQuantity(Integer.toString(Integer.parseInt(groupBuying.getUsedQuantity()) + Integer.parseInt(BuyQuantity)));
            }

            // 更新剩余的商品数量
            if (StrUtil.isNotBlank(groupBuying.getRemainQuantity())) {
                groupBuying.setRemainQuantity(Integer.toString(Integer.parseInt(groupBuying.getRemainQuantity()) - Integer.parseInt(BuyQuantity)));
            } else {
                groupBuying.setRemainQuantity(Integer.toString(Integer.parseInt(groupBuying.getTotalQuantity()) - Integer.parseInt(BuyQuantity)));
            }

            // 追加参与的拍档id
            if (StrUtil.isNotBlank(gb.getJoinMember())) {
                HashSet<String> set = StringUtils.splitAsSet(gb.getJoinMember());
                String JoinMember = StringUtils.join(set, groupBuying.getJoinMember());
                groupBuying.setJoinMember(JoinMember);
            }

            // 追加参与的拍档昵称
            if (StrUtil.isNotBlank(gb.getJoinMemberShow())) {
                HashSet<String> set = StringUtils.splitAsSet(gb.getJoinMemberShow());
                String JoinMemberShow = StringUtils.join(set, groupBuying.getJoinMemberShow());
                groupBuying.setJoinMemberShow(JoinMemberShow);
            }

            // 追加参与团购的记录id
            if (StrUtil.isNotBlank(gb.getAllSubDocunid())) {
                HashSet<String> set = StringUtils.splitAsSet(gb.getAllSubDocunid());
                String AllSubDocunid = StringUtils.join(set, groupBuying.getAllSubDocunid());
                groupBuying.setAllSubDocunid(AllSubDocunid);
            }

        }

        // 状态。0:废弃；1:启用；2:结束；
        if (StrUtil.isNotBlank(gb.getStatus())) {
            groupBuying.setStatus(gb.getStatus());
        }

        // 更新时间
        groupBuying.setUpdateTime(DateUtil.now());

        if (!updateById(groupBuying)) {
            throw new ServiceException("更新团购信息失败！");
        }
        return AjaxResult.success("更新团购信息成功！", groupBuying);
    }

    /**
     * 新增-后台使用
     *
     * @param groupBuying 实体对象
     * @return 新增结果
     */
    @Transactional
    @Override
    public boolean insert(GroupBuying groupBuying) {
        return save(groupBuying);
    }

    /**
     * 获取当前月已结束的拼团记录-后台使用
     *
     * @return
     */
    @Override
    public List<GroupBuying> getCurrentMonthFinishList() {
        return baseMapper.getCurrentMonthFinishList();
    }

    /**
     * 获取上个月已结束的拼团记录-后台使用
     *
     * @return
     */
    @Override
    public List<GroupBuying> getLastMonthFinishList() {
        return baseMapper.getLastMonthFinishList();
    }

    @Override
    public void addPurchaseRecord(String id, int customerNum, int usedQuantity) {
        if (StringUtils.isBlank(id)) {
            throw new ServiceException("数据不存在");
        }
        if (customerNum > usedQuantity) {
            throw new ServiceException("订单总数不能大于累计销量");
        }
        GroupBuying oldGroupBuying = Optional.of(baseMapper.selectById(id))
                .orElseThrow(() -> new ServiceException("团购不存在"));
        if ("2".equals(oldGroupBuying.getStatus())) {
            throw new ServiceException("团购已结束，不可修改");
        }
        BigDecimal foremanReward = new BigDecimal(oldGroupBuying.getDistributionReward());
        // 写入钱包
        for (int i = 1; i <= customerNum; i++) {
            // 计算每笔订单需要加多少
            int sales = usedQuantity / customerNum;
            if (usedQuantity % customerNum != 0 && i == customerNum) {
                // 除不尽 有余数
                sales += usedQuantity % customerNum;
            }
            BigDecimal newForemanReward = foremanReward.multiply(BigDecimal.valueOf(sales));
            Double points = newForemanReward.multiply(BigDecimal.valueOf(100)).doubleValue();
            JSONObject object = new JSONObject();
            object.put("userId", oldGroupBuying.getInitiatorId());// 消费者id
            object.put("points", String.format("%.2f", points));// 保留0位小数
            object.put("reason", oldGroupBuying.getGoodsName() + "团购分销报酬，共" + newForemanReward.setScale(2, RoundingMode.HALF_UP).toString() + "元");
            object.put("sceneId", INTEGRAL_SYSTEM_ID);// 自定义积分体系 ID
            object = getToken(object);
            String result = subPayToWallet(object);
            String code = JSON.parseObject(result).getString("code");
            if (!"200".equals(code)) {
                log.error("给已确认收货的消费者发放团销返利失败！");
                throw new ServiceException("写入钱包失败，请重试");
            }
        }
        // 更改原先的数据
        int newCustomerNum = Integer.parseInt(oldGroupBuying.getCustomerNum()) + customerNum;
        int newUsedQuantity = Integer.parseInt(oldGroupBuying.getUsedQuantity()) + usedQuantity;
        oldGroupBuying.setCustomerNum(String.valueOf(newCustomerNum));
        oldGroupBuying.setUsedQuantity(String.valueOf(newUsedQuantity));
        updateById(oldGroupBuying);
    }

    /** ********************************* 以下是定时任务  start ****************************************** */

    /**
     * 到期自动结束拼团
     * 每天07:10分执行一次
     * 步骤：
     * 1.获取已到期需要结束的拼团记录;
     * 2.结束拼团(即修改状态status = "2")；
     * -----------------------------第3/4点 团销结算，暂不执行 2023-06-25 注释 by lys--------------------------------------
     * 3.记录已到期拼团包含的商品列表；
     * 4.结算拼团;
     *  4.1 根据拼团主文档id获取所有参与团购记录；
     *  4.2.给消费者发放团销返利与通知；
     *      情况1：在团购结束之前已确认收货的消费者：发放消费者团销返利与通知，并更新“sendGroupSalesRewards = 'true'”；
     *      情况2：在团购结束之前已发货，但未确认收货的消费者：发放通知；
     *      情况3：在团购结束之前已下单但平台还没有发货的消费者：发放通知；
     *  4.3.给线上服务商发放团销返利与通知
     * 5.给区域服务商发放通知；
     *
     * @return
     */
    @Scheduled(cron = "${buying.auto_end_group.cron}")
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean automaticEndGroup() throws Exception {

        log.info("********************************");
        log.info("开始执行定时任务：到期自动结束拼团....");

        // 用于存储已到期拼团包含的商品列表
        Map<String, Object> goodsMap = new HashMap<>();

        // 用于存储商品的总销量、团销返利金额等信息
        Map<String, Object> infoMap = new HashMap<>();

        /**
         * 第1步 获取已到期需要结束的拼团记录
         */
        List<GroupBuying> list = baseMapper.getBecomeDueGroupList();
        if (list == null) {
            log.info("没有已到期需要结束的拼团。");
            log.info("执行成功！");
            log.info("********************************");
            return true;
        }

        /**
         * 第2步 结束拼团(即修改状态status = "2")
         */
        if (!updateStatus(list)) {
            throw new ServiceException("第1步：修改已到期拼团的状态为结束出错了。");
        }

        /**
         *
         * 2023-06-25 注释 by lys
         * 注释原因：
         *  暂时不需要给消费者与线上服务商发放团销奖励与通知
         *
         * 第3步 记录已到期拼团包含的商品列表；
         *      记录商品的总销量、团销返利金额等信息

         // 遍历需要结束的拼团
        for (GroupBuying gb : list) {

            // 获取商品信息
            Goods goods = goodsService.getById(gb.getGoodsId());
            if (goods == null || BeanUtil.isEmpty(goods)) {
                log.error("根据商品id（" + gb.getGoodsId() + "）获取不到商品信息，无法进行结算！");
                log.info("执行结束（出错中断）。");
                log.info("********************************");
                throw new ServiceException("根据商品id（" + gb.getGoodsId() + "）获取不到商品信息，无法进行结算！");
            }

            // 1.记录已到期拼团包含的商品列表
            goodsMap.put(gb.getCycle(), goods);

            // 商品总销量 = 商品实际销量 + 商品自定义销量
            long totalBuyQuantity = 0;

            // 商品自定义销量
            if (StrUtil.isNotBlank(goods.getCustomSales()) && Integer.parseInt(goods.getCustomSales()) > 0) {
                totalBuyQuantity += Long.parseLong(goods.getCustomSales());
            }

            // 商品实际销量(本期累计的商品销量)
            long actualSales = groupBuyingSubService.getTotalBuyQuantity(gb.getGoodsId(), gb.getCycle(), "");
            if (actualSales > 0) {
                totalBuyQuantity += actualSales;
            }

            String consumerCashback = "";// 消费者返利金额
            String onlineCashback = "";// 代理服务商返利金额（原线上服务商返利金额）
            String providerCashback = "";// 选址服务商返利金额（原区域服务商返利金额）
            String groupType = "";// 团购类型。百人团/千人团/万人团

            if (totalBuyQuantity > 100 && totalBuyQuantity <= 1000) {
                // 百人团
                consumerCashback = goods.getHundredConsumerCashback();
                onlineCashback = goods.getHundredOnlineCashback();
                providerCashback = goods.getHundredProviderCashback();
                groupType = "百人团";

            } else if (totalBuyQuantity > 1000 && totalBuyQuantity <= 10000) {
                // 千人团
                consumerCashback = goods.getThousandConsumerCashback();
                onlineCashback = goods.getThousandOnlineCashback();
                providerCashback = goods.getThousandProviderCashback();
                groupType = "千人团";

            } else if (totalBuyQuantity > 10000) {
                // 万人团
                consumerCashback = goods.getTenThousandConsumerCashback();
                onlineCashback = goods.getTenThousandOnlineCashback();
                providerCashback = goods.getTenThousandProviderCashback();
                groupType = "万人团";
            }

            JSONObject basicInfoObject = new JSONObject();
            basicInfoObject.put("totalBuyQuantity", totalBuyQuantity);// 商品总销量
            basicInfoObject.put("consumerCashback", consumerCashback);// 消费者返现金额
            basicInfoObject.put("onlineCashback", onlineCashback);// 代理服务商返利金额（原线上服务商返利金额）
            basicInfoObject.put("providerCashback", providerCashback);// 选址服务商返利金额（原区域服务商返利金额）
            basicInfoObject.put("groupType", groupType);// 团购类型。百人团/千人团/万人团
            basicInfoObject.put("foremanReward", goods.getForemanReward());// 分销佣金

            // 2.记录商品的总销量、团销返利金额等信息
            infoMap.put(gb.getId(), basicInfoObject);
        }*/

        /**
         *
         * 2023-06-25 注释 by lys
         * 注释原因：
         *  暂时不需要给消费者与线上服务商发放团销奖励与通知
         *
         *  第4步 结算拼团

         // 遍历需要结束的拼团
        for (GroupBuying gb : list) {

            JSONObject basicInfoObject = (JSONObject) JSONObject.toJSON(infoMap.get(gb.getId()));
            if (basicInfoObject.getLong("totalBuyQuantity") < 100) {
                log.info("此拼团(" + gb.getId() + ")的商品总销量未达到标准，无需要发放团销返利！");
                continue;
            }

            if (!settlementGroup(gb, basicInfoObject)) {
                log.error("根据拼团记录id（" + gb.getId() + "）进行结算出错了！");
                log.info("执行结束（出错中断）。");
                log.info("********************************");
                return false;
            }
        }*/

        /**
         * 第5步 给区域服务商发放通知；
         */
        if (!sendMsgToProvider(goodsMap, infoMap)) {
            log.error("给区域服务商发送结算通知出错了！");
            return false;
        }

        log.info("执行成功！");
        log.info("********************************");
        return true;

    }

    /**
     * 第2步 结束拼团(即修改状态status = "2")
     *
     * @param list 拼团记录
     * @return
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean updateStatus(List<GroupBuying> list) throws Exception {

        for (GroupBuying gb : list) {
            LambdaUpdateWrapper<GroupBuying> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(GroupBuying::getId, gb.getId());
            updateWrapper.eq(GroupBuying::getStatus, "1");// 状态。0:废弃；1:启用；2:结束；
            // 修改拼团状态为：结束
            updateWrapper.set(GroupBuying::getStatus, "2");// 状态
            updateWrapper.set(GroupBuying::getFinishTime, DateUtils.getNow());// 拼团时间结束时间
            updateWrapper.set(GroupBuying::getUpdateTime, DateUtils.getNow());// 更新时间

            if (!update(updateWrapper)) {
                log.error("第1步：结束拼团出错了，出错记录id：" + gb.getId() + "。");
                log.info("执行结束（出错中断）。");
                log.info("********************************");
                return false;
            }
        }
        return true;
    }

    /**
     * 第4步 结算拼团
     * 步骤：
     * 1.根据拼团主文档id获取所有参与团购记录；
     * 2.给消费者发放团销返利与通知；
     *  情况1：在团购结束之前已确认收货的消费者：发放消费者团销返利与通知，并更新“sendGroupSalesRewards = 'true'”；
     *  情况2：在团购结束之前已发货，但未确认收货的消费者：发放通知；
     *  情况3：在团购结束之前已下单但平台还没有发货的消费者：发放通知；
     * 3.给线上服务商发放团销返利与通知
     *
     * @param gb              拼团详情
     * @param basicInfoObject 商品的总销量、团销返利金额等信息
     * @return
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean settlementGroup(GroupBuying gb, JSONObject basicInfoObject) {

        // 1.根据拼团主文档id获取所有参与团购记录
        List<GroupBuyingSub> allJoinList = groupBuyingSubService.getAllJoinList(gb.getId());
        if (allJoinList == null || allJoinList.size() < 0) {
            log.info("根据拼团id(" + gb.getId() + ")获取不到参与记录，无需要进行结算！");
            return true;
        }

        // 消费者团销返现金额
        double consumerCashback = basicInfoObject.getDouble("consumerCashback");

        // 代理服务商的团销返利金额
        double onlineCashback = basicInfoObject.getDouble("onlineCashback");

        // 选址服务商的团销返利金额
        double providerCashback = basicInfoObject.getDouble("providerCashback");

        // 代理服务商的分销返利金额
        double foremanReward = basicInfoObject.getDouble("foremanReward");

        // 2.给消费者发放团销返利与通知
        if (consumerCashback > 0) {
            // 遍历所有参与团购记录
            for (GroupBuyingSub gbSub : allJoinList) {
                // 购买数量
                double buyQuantity = 0.0;
                if (StrUtil.isNotBlank(gbSub.getBuyQuantity())) {
                    buyQuantity = Integer.parseInt(gbSub.getBuyQuantity());
                }

                // 消费者团销报酬总额 = 消费者团销返利金额 * 购买数量；
                double consumer_reward = consumerCashback * buyQuantity;

                // 消费者团销报酬总额需要增加的积分 = 消费者团销报酬总额 * 100.0
                double consumer_points = consumer_reward * 100.0;

                /**
                 * 情况1：在团购结束之前已确认收货的消费者
                 *  1.发放消费者团销返利；
                 *  2.发送通知。
                 *  3.更新“sendGroupSalesRewards = 'true'”,是否发放团销奖励(消费者/线上服务商)。true:已发放；false:未发放
                 */
                if (("已完成".equals(gbSub.getOrderStatus()) || "已评价".equals(gbSub.getOrderStatus())) && consumer_reward > 0 && consumer_points > 0) {
                    // 1.发放消费者团销返利；
                    // 调用积分系统：转入钱包
                    JSONObject object = new JSONObject();
                    object.put("userId", gbSub.getPartnerId());// 消费者id
                    object.put("points", String.format("%.0f", consumer_points));// 保留0位小数
                    object.put("reason", gbSub.getGoodsName() + "团购(消费者)团销返利，共" + String.format("%.2f", consumer_reward) + "元");
                    object.put("sceneId", INTEGRAL_SYSTEM_ID);// 自定义积分体系 ID
                    object = getToken(object);
                    String result = subPayToWallet(object);
                    String code = JSON.parseObject(result).getString("code");
                    if (!"200".equals(code)) {
                        log.error("给已确认收货的消费者发放团销返利失败！");
                        return false;
                    }

                    // 2.发送通知。
                    HashMap<String, Object> params = new HashMap<>();
                    params.put("first", "亲，本场活动已结束，活动累计总销量" + basicInfoObject.getLong("totalBuyQuantity") + "件，已成" + basicInfoObject.getString("groupType") + "。您参与购买" + gbSub.getBuyQuantity() + "件商品，获返现" + String.format("%.2f", consumer_reward) + "元。");
                    params.put("keyword1", gbSub.getTaskNumber());// 订单编号
                    params.put("keyword2", gbSub.getGoodsName());// 商品名称
                    params.put("remark", "点击查看收益详情。平台现有大量零食/美妆/洗护/母婴/百货等商品销售资源，佣金非常可观，欢迎前往选品！");
                    params.put("pagepath", "/pages/index/incomeIndex");// （链接到【收益页面】）
                    params.put("pageurl", "/pages/index/incomeIndex");// （链接到【收益页面】）
                    params.put("docUnid", gbSub.getId());
                    params.put("userId", gbSub.getPartnerId());
                    JSONObject obj = sendMsg(params);
                    if (!"200".equals(obj.getString("code"))) {
                        log.error("给已确认收货的消费者发送通知失败！");
                        return false;
                    }

                    // 3.更新“sendGroupSalesRewards = 'true'”,是否发放团销奖励(消费者/线上服务商)。true:已发放；false:未发放
                    LambdaUpdateWrapper<GroupBuyingSub> updateWrapper = new LambdaUpdateWrapper<>();
                    updateWrapper.eq(GroupBuyingSub::getId, gbSub.getId());
                    updateWrapper.set(GroupBuyingSub::getSendGroupSalesRewards, "true");
                    updateWrapper.set(GroupBuyingSub::getUpdateTime, DateUtil.now());
                    if (!groupBuyingSubService.update(updateWrapper)) {
                        throw new ServiceException("更新sendGroupSalesRewards字段出错了！");
                    }

                }

                /**
                 * 情况2：在团购结束之前已发货，但未确认收货的消费者
                 *  1.发送通知。
                 */
                if ("已发货".equals(gbSub.getOrderStatus()) && consumer_reward > 0) {
                    // 1.发送通知。
                    HashMap<String, Object> params = new HashMap<>();
                    params.put("first", "亲，本场活动已结束，活动累计总销量" + basicInfoObject.getLong("totalBuyQuantity") + "件，已成" + basicInfoObject.getString("groupType") + "。您参与购买" + gbSub.getBuyQuantity() + "件商品，预计返现" + String.format("%.2f", consumer_reward) + "元，确认收货后立即返现！");
                    params.put("keyword1", gbSub.getTaskNumber());// 订单编号
                    params.put("keyword2", gbSub.getGoodsName());// 商品名称
                    params.put("remark", "点击查看订单详情。平台现有大量零食/美妆/洗护/母婴/百货等商品销售资源，佣金非常可观，欢迎前往选品！");
                    params.put("pagepath", "/pages_private_scene2/GroupPurchase/OrderDetails?recordId=" + gbSub.getId() + "&scenario=2");// （链接到【订单详情】）
                    params.put("pageurl", "/pages_private_scene2/GroupPurchase/OrderDetails?recordId=" + gbSub.getId() + "&scenario=2");// （链接到【订单详情】）
                    params.put("docUnid", gbSub.getId());
                    params.put("userId", gbSub.getPartnerId());
                    JSONObject obj = sendMsg(params);
                    if (!"200".equals(obj.getString("code"))) {
                        log.error("给已发货但未确认收货的消费者发送通知失败！");
                        return false;
                    }
                }

                /**
                 * 情况3：在团购结束之前已下单但平台还没有发货的消费者
                 *  1.发送通知。
                 */
                if ("已付款".equals(gbSub.getOrderStatus()) && consumer_reward > 0) {
                    // 1.发送通知。
                    HashMap<String, Object> params = new HashMap<>();
                    params.put("first", "亲，本场活动已结束，活动累计总销量" + basicInfoObject.getLong("totalBuyQuantity") + "件，已成" + basicInfoObject.getString("groupType") + "返现标准。您参与购买" + gbSub.getBuyQuantity() + "件商品，预计返现" + String.format("%.2f", consumer_reward) + "元！目前订单较多，请您耐心等待物流通知，确认收货后立即返现！");
                    params.put("keyword1", gbSub.getTaskNumber());// 订单编号
                    params.put("keyword2", gbSub.getGoodsName());// 商品名称
                    params.put("remark", "点击查看订单详情。平台现有大量零食/美妆/洗护/母婴/百货等商品销售资源，佣金非常可观，欢迎前往选品！");
                    params.put("pagepath", "/pages_private_scene2/GroupPurchase/OrderDetails?recordId=" + gbSub.getId() + "&scenario=3");// （链接到【订单详情】）
                    params.put("pageurl", "/pages_private_scene2/GroupPurchase/OrderDetails?recordId=" + gbSub.getId() + "&scenario=3");// （链接到【订单详情】）
                    params.put("docUnid", gbSub.getId());
                    params.put("userId", gbSub.getPartnerId());
                    JSONObject obj = sendMsg(params);
                    if (!"200".equals(obj.getString("code"))) {
                        log.error("给已付款但发货的消费者发送通知失败！");
                        return false;
                    }
                }
            }
        } else {
            log.info("【消费者返现金额(" + consumerCashback + ")】小于零或为空，无需要进行结算！");
            return true;
        }

        // 3.给线上服务商发放团销返利与通知
        if (onlineCashback > 0) {

            long completedGoodsQuantity = 0;// 已完成订单的商品数量
            long totalGoodsQuantity = 0;// 所有订单的商品数量

            double completedForemanReward = 0.0;// 已发放的分销佣金

            // 遍历所有参与团购记录，统计订单是否全部已完成
            for (GroupBuyingSub gbSub : allJoinList) {

                // 是否发放线上服务商分销报酬。true:已发放；false:未发放
                if (Boolean.parseBoolean(gbSub.getSendForemanReward())) {
                    completedForemanReward += foremanReward * Integer.parseInt(gbSub.getBuyQuantity());
                }

                if ("已完成".equals(gbSub.getOrderStatus()) || "已评价".equals(gbSub.getOrderStatus())) {
                    completedGoodsQuantity += Long.parseLong(gbSub.getBuyQuantity());

                }
                totalGoodsQuantity += Long.parseLong(gbSub.getBuyQuantity());
            }

            // 要发放的代理服务商团销报酬总额 = 代理服务商团销返利金额 * 此拼团【已完成】订单的商品购买数量；
            double online_reward = onlineCashback * completedGoodsQuantity;

            // 要发放的代理服务商团销报酬需要增加的积分 = 线上服务商分销报酬总额 * 100.0
            double online_points = online_reward * 100.0;

            // 预计返利 = (代理服务商团销返利金额 * 此拼团【所有的】订单的商品购买数量) + (分销佣金 * 此拼团【所有的】订单的商品购买数量)；
            double allReward = (onlineCashback * totalGoodsQuantity) + (foremanReward * totalGoodsQuantity);

            // 已到账（现在发放的） = 要发放的代理服务商团销报酬总额 + 已发放的分销佣金；
            double receivedReward = online_reward + completedForemanReward;

            // 线上服务商团销返利总额大于零才需要发放通知与报酬
            if (allReward > 0) {
                // 1.发放线上服务商团销返利
                JSONObject object = new JSONObject();
                object.put("userId", gb.getInitiatorId());// 线上服务商
                object.put("points", String.format("%.0f", online_points));// 保留0位小数
                object.put("reason", gb.getGoodsName() + "团购(代理服务商)团销返利，共" + String.format("%.2f", online_reward) + "元");
                object.put("sceneId", INTEGRAL_SYSTEM_ID);// 自定义积分体系 ID
                object = getToken(object);
                String result = subPayToWallet(object);
                String code = JSON.parseObject(result).getString("code");
                if (!"200".equals(code)) {
                    log.error("给代理服务商发放团销返利失败！");
                    return false;
                }

                // 获取【发起人】的区域服务商编号
                Provider provider = providerService.getByUserId(gb.getInitiatorId());
                String initiatorProviderId = "";
                if (provider != null) {
                    initiatorProviderId = "N0." + provider.getProviderId();
                }

                HashMap<String, Object> paramsDiad = new HashMap<>();
                paramsDiad.put("first", "【区域服务商" + initiatorProviderId + "】恭喜您，活动已成" + basicInfoObject.getString("groupType") + "！您售出" + totalGoodsQuantity + "件商品，本次预计返利" + String.format("%.2f", allReward) + "元，其中" + String.format("%.2f", receivedReward) + "元已到账。");
                paramsDiad.put("keyword1", gb.getTaskNumber());// 订单编号
                paramsDiad.put("keyword2", gb.getGoodsName());// 商品名称
                paramsDiad.put("remark", "点击查看销售进度。温馨提示：售出的订单需确认收货，才能获得" + basicInfoObject.getString("groupType") + "返现");
                paramsDiad.put("pagepath", "/pages_tabbar_contact/personal_contact/SolitaireList");// （链接到【我的-代理销售-代理销售商品列表】）
                paramsDiad.put("pageurl", "/pages_tabbar_contact/personal_contact/SolitaireList");// （链接到【我的-代理销售-代理销售商品列表】）
                paramsDiad.put("docUnid", gb.getId());
                paramsDiad.put("userId", gb.getInitiatorId());
                JSONObject objectDiad = sendMsg(paramsDiad);
                if (objectDiad.isEmpty() || !"200".equals(objectDiad.getString("code"))) {
                    throw new ServiceException("给代理服务商发送通知失败！");
                }
            } else {
                log.info("【代理务商团销返利总额(" + allReward + ")】小于零或为空，无需要进行结算！");
                return true;
            }
        } else {
            log.info("【代理服务商团销返现金额(" + onlineCashback + ")】小于零或为空，无需要进行结算！");
            return true;
        }

        log.info("结算拼团(" + gb.getId() + ")执行成功！");
        return true;

    }

    /**
     * 第4步 给区域服务商发放通知
     *
     * @param goodsMap 已到期的商品信息
     * @param infoMap  商品的总销量、团销返利金额等信息
     * @return
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean sendMsgToProvider(Map<String, Object> goodsMap, Map<String, Object> infoMap) {

        System.out.println("给区域服务商发放通知................");

        // 遍历商品map
        Iterator<Map.Entry<String, Object>> it = goodsMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, Object> next = it.next();
            String cycleId = next.getKey();// 周期串
            JSONObject goodsObject = (JSONObject) JSONObject.toJSON(next.getValue());// 商品详情

            // 1.自定义销量
            long customSales = 0;
            if (StrUtil.isNotBlank(goodsObject.getString("customSales"))) {
                customSales = Long.parseLong(goodsObject.getString("customSales"));
            }

            // 2.获取当前周期商品实际销量
            long totalBuyQuantity = groupBuyingSubService.getTotalBuyQuantity(goodsObject.getString("goodsId"), cycleId, "");

            totalBuyQuantity = totalBuyQuantity + customSales;

            if (totalBuyQuantity < 100) {
                log.info("【" + goodsObject.getString("goodsName") + "(" + goodsObject.getString("goodsId") + "）】销量未达到标准，无需要给区域服务商发放通知！");
                continue;
            }

            String providerCashback = "";// 区域服务商返现金额
            String groupType = "";// 团购类型。百人团/千人团/万人团
            if (totalBuyQuantity > 100 && totalBuyQuantity <= 1000) {
                providerCashback = goodsObject.getString("hundredProviderCashback");
                groupType = "百人团";
            } else if (totalBuyQuantity > 1000 && totalBuyQuantity <= 10000) {
                providerCashback = goodsObject.getString("thousandProviderCashback");
                groupType = "千人团";
            } else if (totalBuyQuantity > 10000) {
                providerCashback = goodsObject.getString("tenThousandProviderCashback");
                groupType = "万人团";
            }

            if (StrUtil.isNotBlank(providerCashback) && Double.parseDouble(providerCashback) > 0) {
                // 获取当前周期下某商品的所有购买记录
                List<GroupBuyingSub> allBuyingList = groupBuyingSubService.getAllBuyingList(goodsObject.getString("goodsId"), cycleId);

                // 存储每个区域服务商的订单数量、商品销量、奖励
                Map<String, Object> map = new HashMap<String, Object>();

                // 遍历所有购买记录
                for (GroupBuyingSub gbSub : allBuyingList) {
                    String supplierId = gbSub.getSupplierId();// 区域服务商id

                    // 获取区域商信息
                    JSONObject supplierObject = JSONObject.parseObject(gbSub.getSupplier());
                    String providerId = "";
                    if (StrUtil.isNotBlank(supplierObject.getString("providerId"))) {
                        providerId = "N0." + supplierObject.getString("providerId");
                    }

                    // 判断key是否存在
                    if (map.containsKey(supplierId)) {
                        // 存在，则重新计算object
                        JSONObject object = (JSONObject) map.get(supplierId);
                        object.put("orderNum", object.getLong("orderNum") + 1);// 订单数量 + 1
                        object.put("goodsNum", object.getLong("goodsNum") + Long.parseLong(gbSub.getBuyQuantity()));// 商品销量
                        object.put("reward", object.getDouble("reward") + (Double.parseDouble(providerCashback) * Long.parseLong(gbSub.getBuyQuantity())));// 奖励
                        object.put("providerId", providerId);// 区域服务商编号
                        map.put(supplierId, object);

                    } else {
                        JSONObject object = new JSONObject();
                        object.put("orderNum", 1);// 订单数量
                        object.put("goodsNum", Long.parseLong(gbSub.getBuyQuantity()));// 商品销量
                        object.put("reward", Double.parseDouble(providerCashback) * Long.parseLong(gbSub.getBuyQuantity()));// 奖励
                        object.put("providerId", providerId);// 区域服务商编号
                        map.put(supplierId, object);
                    }
                }

                // 遍历map对象
                for (Map.Entry<String, Object> entry : map.entrySet()) {

                    String supplierId = entry.getKey();
                    // JSONObject object = (JSONObject) JSONObject.toJSON(entry.getValue());
                    JSONObject object = JSON.parseObject(JSON.toJSONString(entry.getValue()), JSONObject.class);

                    // 订单不为零，且报酬不为零，才需要发通知
                    if (object.getLong("orderNum") > 0 && object.getDouble("reward") > 0) {

                        // 给区域服务商发送通知。
                        HashMap<String, Object> params = new HashMap<>();
//                        params.put("first", "【区域服务商" + object.getString("providerId") + "】恭喜您，活动已成" + groupType + "！您售出" + object.getLong("goodsNum") + "件商品，本次预计返利" + String.format("%.2f", allReward) + "元，其中" + String.format("%.2f", receivedReward) + "元已到账。");
                        params.put("first", "【区域服务商" + object.getString("providerId") + "】亲，本场活动已结束，活动累计总销量" + totalBuyQuantity + "件，已成" + groupType + "。您所在的区域产生" + object.getLong("orderNum") + "个订单，合计" + object.getLong("goodsNum") + "件商品，预计获得平台奖励" + String.format("%.2f", object.getDouble("reward")) + "元！");
                        params.put("keyword1", cycleId);// 订单编号(周期串)
                        params.put("keyword2", goodsObject.getString("goodsName"));// 商品名称
                        params.put("remark", "月底统一结算并发放奖励！您所在区域发现不少潜在消费者，建议您发起商品销售，平台现有大量零食/美妆/洗护/母婴/百货等商品销售资源，佣金非常可观，欢迎前往选品！");
                        params.put("pagepath", "/pages/index/evaluationIndex");// （链接到【平台-生活】）
                        params.put("pageurl", "/pages/index/evaluationIndex");// （链接到【平台-生活】）
                        params.put("docUnid", "");
                        params.put("userId", supplierId);

                        JSONObject obj = sendMsg(params);
                        if (!"200".equals(obj.getString("code"))) {
                            log.error("给区域服务商（" + supplierId + "）发送通知失败！");
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }

    /**
     * 到期计算团销返利（直接购买商品的情况）
     * 每天07:20分执行一次
     * 步骤：
     * 1.获取所有直接购买商品的记录（未结算的记录）;
     * 2.获取需结算的记录；
     * 3.给消费者发放团销返利与通知；
     * 4.给选址服务商发放结算通知;
     *
     * @return
     */
//    @Scheduled(cron = "${buying.auto_end_goods.cron}")
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean automaticEndGoods2() throws Exception {

        log.info("********************************");
        log.info("开始执行定时任务：到期计算团销返利（直接购买商品的情况）....");

        /**
         * 第1步 获取所有直接购买商品的记录（未结算的记录）
         */
        LambdaQueryWrapper<GroupBuyingSub> wrapper = new LambdaQueryWrapper<>();
        wrapper.ne(GroupBuyingSub::getSendGroupSalesRewards, "true");// 是否发放团销奖励。true:已发放；false:未发放
        wrapper.gt(GroupBuyingSub::getCreateTime, "2023-05-01 23:59:59");

        List<GroupBuyingSub> list = groupBuyingSubService.list(wrapper);
        if (list.size() <= 0) {
            log.info("没有周期已结束的商品，无需结算。");
            return true;
        }

        // 记录包含的商品id，当前周期串
        HashMap<String, String> map = new HashMap<>();

        // 用于存储商品的总销量、团销返利金额等信息
        Map<String, Object> infoMap = new HashMap<>();

        for (GroupBuyingSub groupBuyingSub : list) {
            // 判断key是否存在
            if (map.containsKey(groupBuyingSub.getGoodsId())) {
                // 存在
                continue;
            } else {
                // 记录包含的商品id，当前周期串
                map.put(groupBuyingSub.getGoodsId(), goodsService.cycle(Long.parseLong(groupBuyingSub.getGoodsId())));

                Goods goods = goodsService.getById(groupBuyingSub.getGoodsId());
                if (goods == null || BeanUtil.isEmpty(goods)) {
                    log.info("根据商品id(" + groupBuyingSub.getGoodsId() + ")获取不到商品信息！");
                    return false;
                }

                // 商品总销量 = 商品实际销量 + 商品自定义销量
                long totalBuyQuantity = 0;

                // 商品自定义销量
                if (StrUtil.isNotBlank(goods.getCustomSales()) && Integer.parseInt(goods.getCustomSales()) > 0) {
                    totalBuyQuantity += Long.parseLong(goods.getCustomSales());
                }

                // 商品实际销量(本期累计的商品销量)
                long actualSales = groupBuyingSubService.getTotalBuyQuantity(groupBuyingSub.getGoodsId(), groupBuyingSub.getCycle(), "");
                if (actualSales > 0) {
                    totalBuyQuantity += actualSales;
                }

                String consumerCashback = "";// 消费者返利金额
                String providerCashback = "";// 选址服务商返利金额（原区域服务商返利金额）
                String groupType = "";// 团购类型。百人团/千人团/万人团

                if (totalBuyQuantity > 100 && totalBuyQuantity <= 1000) {
                    // 百人团
                    consumerCashback = goods.getHundredConsumerCashback();
                    providerCashback = goods.getHundredProviderCashback();
                    groupType = "百人团";

                } else if (totalBuyQuantity > 1000 && totalBuyQuantity <= 10000) {
                    // 千人团
                    consumerCashback = goods.getThousandConsumerCashback();
                    providerCashback = goods.getThousandProviderCashback();
                    groupType = "千人团";

                } else if (totalBuyQuantity > 10000) {
                    // 万人团
                    consumerCashback = goods.getTenThousandConsumerCashback();
                    providerCashback = goods.getTenThousandProviderCashback();
                    groupType = "万人团";
                }

                JSONObject basicInfoObject = new JSONObject();
                basicInfoObject.put("totalBuyQuantity", totalBuyQuantity);// 商品总销量
                basicInfoObject.put("consumerCashback", consumerCashback);// 消费者返现金额
                basicInfoObject.put("providerCashback", providerCashback);// 选址服务商返利金额（原区域服务商返利金额）
                basicInfoObject.put("groupType", groupType);// 团购类型。百人团/千人团/万人团
                basicInfoObject.put("foremanReward", goods.getForemanReward());// 分销佣金

                // 2.记录商品的总销量、团销返利金额等信息
                infoMap.put(groupBuyingSub.getGoodsId(), basicInfoObject);
            }
        }

        /**
         * 第2步 获取需结算的记录
         * 根据“购买记录中的周期串”与“当前周期串”判断商品周期是否已结束
         * 若不一致则表示周期已结束，则记录直接购买记录
         */
        List<GroupBuyingSub> newlist = new ArrayList<>();
        for (GroupBuyingSub groupBuyingSub : list) {
            String currentCycle = map.get(groupBuyingSub.getGoodsId());// 当前周期
            boolean flag = false;// 当前周期是否大于历史周期。大于:true；小于:false；
            // 周期串如： 230052-1630915779874603010-1237643653
            if (StrUtil.isNotBlank(groupBuyingSub.getCycle()) && StrUtil.isNotBlank(currentCycle)) {
                String whichCycle = groupBuyingSub.getCycle().split("-")[0];
                String currentWhichCycle = currentCycle.split("-")[0];
                if (Integer.parseInt(currentWhichCycle) > Integer.parseInt(whichCycle)) {
                    // 如果当前周期，大于历史周期，表示已经到了下个周期
                    flag = true;
                }
            }

            if (currentCycle.equals(groupBuyingSub.getCycle()) && !flag) {
                // 周期串一样，则表示周期未结束
                continue;
            } else {
                // 记录需要结算的直接购买记录
                newlist.add(groupBuyingSub);
            }
        }
        if (newlist.size() <= 0) {
            log.info("没有周期已结束的商品，无需结算。");
            return true;
        }

        /**
         * 第3步 给消费者发放团销返利与通知；
         */
        for (GroupBuyingSub gbSub : newlist) {
            JSONObject basicInfoObject = (JSONObject) JSONObject.toJSON(infoMap.get(gbSub.getGoodsId()));
            if (basicInfoObject.getLong("totalBuyQuantity") < 100) {
                log.info("此商品(" + gbSub.getId() + ")的商品总销量未达到标准，无需要发放团销返利！");
                continue;
            }
            if (!settlementGoods2(gbSub, basicInfoObject)) {
                log.info("执行结束（出错中断）。");
                log.info("********************************");
                return false;
            }
        }

        /**
         * 第4步 给选址服务商发放结算通知；
         */
        if (!sendMsgToProvider2(newlist, map, infoMap)) {
            log.error("给选址服务商发送结算通知出错了！");
            return false;
        }

        log.info("执行成功！");
        log.info("********************************");
        return true;
    }

    /**
     * 第3步 给消费者发放团销返利与通知
     *
     * @param gbSub           购买记录详情
     * @param basicInfoObject 商品的总销量、团销返利金额等信息
     * @return
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean settlementGoods2(GroupBuyingSub gbSub, JSONObject basicInfoObject) {

        // 消费者团销返现金额
        double consumerCashback = basicInfoObject.getDouble("consumerCashback");

        // 选址服务商的团销返利金额
        double providerCashback = basicInfoObject.getDouble("providerCashback");

        // 2.给消费者发放团销返利与通知
        if (consumerCashback > 0) {

            // 购买数量
            double buyQuantity = 0.0;
            if (StrUtil.isNotBlank(gbSub.getBuyQuantity())) {
                buyQuantity = Integer.parseInt(gbSub.getBuyQuantity());
            }

            // 消费者团销报酬总额 = 消费者团销返利金额 * 购买数量；
            double consumer_reward = consumerCashback * buyQuantity;

            // 消费者团销报酬总额需要增加的积分 = 消费者团销报酬总额 * 100.0
            double consumer_points = consumer_reward * 100.0;

            /**
             * 情况1：在团购结束之前已确认收货的消费者
             *  1.发放消费者团销返利；
             *  2.发送通知。
             *  3.更新“sendGroupSalesRewards = 'true'”,是否发放团销奖励(消费者/线上服务商)。true:已发放；false:未发放
             */
            if (("已完成".equals(gbSub.getOrderStatus()) || "已评价".equals(gbSub.getOrderStatus())) && consumer_reward > 0 && consumer_points > 0) {
                // 1.发放消费者团销返利；
                // 调用积分系统：转入钱包
                JSONObject object = new JSONObject();
                object.put("userId", gbSub.getPartnerId());// 消费者id
                object.put("points", String.format("%.0f", consumer_points));// 保留0位小数
                object.put("reason", gbSub.getGoodsName() + "团购(消费者)团销返利，共" + String.format("%.2f", consumer_reward) + "元");
                object.put("sceneId", INTEGRAL_SYSTEM_ID);// 自定义积分体系 ID
                object = getToken(object);
                String result = subPayToWallet(object);
                String code = JSON.parseObject(result).getString("code");
                if (!"200".equals(code)) {
                    throw new ServiceException("给已确认收货的消费者发放团销返利失败！");
                }

                // 2.发送通知。
                HashMap<String, Object> params = new HashMap<>();
                params.put("first", "亲，本场活动已结束，活动累计总销量" + basicInfoObject.getLong("totalBuyQuantity") + "件，已成" + basicInfoObject.getString("groupType") + "。您参与购买" + gbSub.getBuyQuantity() + "件商品，获返现" + String.format("%.2f", consumer_reward) + "元。");
                params.put("keyword1", gbSub.getTaskNumber());// 订单编号
                params.put("keyword2", gbSub.getGoodsName());// 商品名称
                params.put("remark", "点击查看收益详情。平台现有大量零食/美妆/洗护/母婴/百货等商品销售资源，佣金非常可观，欢迎前往选品！");
                params.put("pagepath", "/pages/index/incomeIndex");// （链接到【收益页面】）
                params.put("pageurl", "/pages/index/incomeIndex");// （链接到【收益页面】）
                params.put("docUnid", gbSub.getId());
                params.put("userId", gbSub.getPartnerId());
                JSONObject obj = sendMsg(params);
                if (!"200".equals(obj.getString("code"))) {
                    throw new ServiceException("给已确认收货的消费者发送通知失败！");
                }

                // 3.更新“sendGroupSalesRewards = 'true'”,是否发放团销奖励(消费者/线上服务商)。true:已发放；false:未发放
                LambdaUpdateWrapper<GroupBuyingSub> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(GroupBuyingSub::getId, gbSub.getId());
                updateWrapper.set(GroupBuyingSub::getSendGroupSalesRewards, "true");
                updateWrapper.set(GroupBuyingSub::getUpdateTime, DateUtil.now());
                if (!groupBuyingSubService.update(updateWrapper)) {
                    throw new ServiceException("更新sendGroupSalesRewards字段出错了！");
                }
            }

            /**
             * 情况2：在团购结束之前已发货，但未确认收货的消费者
             *  1.发送通知。
             */
            if ("已发货".equals(gbSub.getOrderStatus()) && consumer_reward > 0) {
                // 1.发送通知。
                HashMap<String, Object> params = new HashMap<>();
                params.put("first", "亲，本场活动已结束，活动累计总销量" + basicInfoObject.getLong("totalBuyQuantity") + "件，已成" + basicInfoObject.getString("groupType") + "。您参与购买" + gbSub.getBuyQuantity() + "件商品，预计返现" + String.format("%.2f", consumer_reward) + "元，确认收货后立即返现！");
                params.put("keyword1", gbSub.getTaskNumber());// 订单编号
                params.put("keyword2", gbSub.getGoodsName());// 商品名称
                params.put("remark", "点击查看订单详情。平台现有大量零食/美妆/洗护/母婴/百货等商品销售资源，佣金非常可观，欢迎前往选品！");
                params.put("pagepath", "/pages_private_scene2/GroupPurchase/OrderDetails?recordId=" + gbSub.getId() + "&scenario=2");// （链接到【订单详情】）
                params.put("pageurl", "/pages_private_scene2/GroupPurchase/OrderDetails?recordId=" + gbSub.getId() + "&scenario=2");// （链接到【订单详情】）
                params.put("docUnid", gbSub.getId());
                params.put("userId", gbSub.getPartnerId());
                JSONObject obj = sendMsg(params);
                if (!"200".equals(obj.getString("code"))) {
                    throw new ServiceException("给已发货但未确认收货的消费者发送通知失败！");
                }
            }

            /**
             * 情况3：在团购结束之前已下单但平台还没有发货的消费者
             *  1.发送通知。
             */
            if ("已付款".equals(gbSub.getOrderStatus()) && consumer_reward > 0) {
                // 1.发送通知。
                HashMap<String, Object> params = new HashMap<>();
                params.put("first", "亲，本场活动已结束，活动累计总销量" + basicInfoObject.getLong("totalBuyQuantity") + "件，已成" + basicInfoObject.getString("groupType") + "返现标准。您参与购买" + gbSub.getBuyQuantity() + "件商品，预计返现" + String.format("%.2f", consumer_reward) + "元！目前订单较多，请您耐心等待物流通知，确认收货后立即返现！");
                params.put("keyword1", gbSub.getTaskNumber());// 订单编号
                params.put("keyword2", gbSub.getGoodsName());// 商品名称
                params.put("remark", "点击查看订单详情。平台现有大量零食/美妆/洗护/母婴/百货等商品销售资源，佣金非常可观，欢迎前往选品！");
                params.put("pagepath", "/pages_private_scene2/GroupPurchase/OrderDetails?recordId=" + gbSub.getId() + "&scenario=3");// （链接到【订单详情】）
                params.put("pageurl", "/pages_private_scene2/GroupPurchase/OrderDetails?recordId=" + gbSub.getId() + "&scenario=3");// （链接到【订单详情】）
                params.put("docUnid", gbSub.getId());
                params.put("userId", gbSub.getPartnerId());
                JSONObject obj = sendMsg(params);
                if (!"200".equals(obj.getString("code"))) {
                    throw new ServiceException("给已付款但发货的消费者发送通知失败！");
                }
            }
        } else {
            log.info("【消费者返现金额(" + consumerCashback + ")】小于零或为空，无需要进行结算！");
            return true;
        }

        log.info("结算给消费者发放团销返利与通知执行成功！");
        return true;

    }

    /**
     * 第4步 给区域服务商发放通知
     *
     * @param newlist  需要结算的购买记录
     * @param goodsMap 已到期的商品信息
     * @param infoMap  商品的总销量、团销返利金额等信息
     * @return
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean sendMsgToProvider2(List<GroupBuyingSub> newlist, Map<String, String> goodsMap, Map<String, Object> infoMap) {

        System.out.println("给区域服务商发放通知................");

        // 遍历商品map
        Iterator<Map.Entry<String, String>> it = goodsMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> next = it.next();
            String goodsId = next.getKey();// 商品id
            String cycleId = next.getValue();// 周期串
            Goods goods = goodsService.getById(goodsId);
            if (goods == null || BeanUtil.isEmpty(goods)) {
                throw new ServiceException("根据商品id(" + goodsId + ")获取不到商品信息！");
            }

            // 1.自定义销量
            long customSales = 0;
            if (StrUtil.isNotBlank(goods.getCustomSales())) {
                customSales = Long.parseLong(goods.getCustomSales());
            }

            // 2.获取当前周期商品实际销量
            long totalBuyQuantity = groupBuyingSubService.getTotalBuyQuantity(goodsId, cycleId, "");

            totalBuyQuantity = totalBuyQuantity + customSales;

            if (totalBuyQuantity < 100) {
                log.info("【" + goods.getGoodsName() + "(" + goods.getGoodsId() + "）】销量未达到标准，无需要给区域服务商发放通知！");
                continue;
            }

            String providerCashback = "";// 区域服务商返现金额
            String groupType = "";// 团购类型。百人团/千人团/万人团
            if (totalBuyQuantity > 100 && totalBuyQuantity <= 1000) {
                providerCashback = goods.getHundredProviderCashback();
                groupType = "百人团";
            } else if (totalBuyQuantity > 1000 && totalBuyQuantity <= 10000) {
                providerCashback = goods.getThousandProviderCashback();
                groupType = "千人团";
            } else if (totalBuyQuantity > 10000) {
                providerCashback = goods.getTenThousandProviderCashback();
                groupType = "万人团";
            }

            if (StrUtil.isNotBlank(providerCashback) && Double.parseDouble(providerCashback) > 0) {

                // 存储每个区域服务商的订单数量、商品销量、奖励
                Map<String, Object> map = new HashMap<String, Object>();

                // 遍历所有购买记录
                for (GroupBuyingSub gbSub : newlist) {
                    String supplierId = gbSub.getSupplierId();// 区域服务商id

                    // 获取区域商信息
                    JSONObject supplierObject = JSONObject.parseObject(gbSub.getSupplier());
                    String providerId = "";
                    if (StrUtil.isNotBlank(supplierObject.getString("providerId"))) {
                        providerId = "N0." + supplierObject.getString("providerId");
                    }

                    // 判断key是否存在
                    if (map.containsKey(supplierId)) {
                        // 存在，则重新计算object
                        JSONObject object = (JSONObject) map.get(supplierId);
                        object.put("orderNum", object.getLong("orderNum") + 1);// 订单数量 + 1
                        object.put("goodsNum", object.getLong("goodsNum") + Long.parseLong(gbSub.getBuyQuantity()));// 商品销量
                        object.put("reward", object.getDouble("reward") + (Double.parseDouble(providerCashback) * Long.parseLong(gbSub.getBuyQuantity())));// 奖励
                        object.put("providerId", providerId);// 区域服务商编号
                        map.put(supplierId, object);

                    } else {
                        JSONObject object = new JSONObject();
                        object.put("orderNum", 1);// 订单数量
                        object.put("goodsNum", Long.parseLong(gbSub.getBuyQuantity()));// 商品销量
                        object.put("reward", Double.parseDouble(providerCashback) * Long.parseLong(gbSub.getBuyQuantity()));// 奖励
                        object.put("providerId", providerId);// 区域服务商编号
                        map.put(supplierId, object);
                    }
                }

                // 遍历map对象
                for (Map.Entry<String, Object> entry : map.entrySet()) {

                    String supplierId = entry.getKey();
                    // JSONObject object = (JSONObject) JSONObject.toJSON(entry.getValue());
                    JSONObject object = JSON.parseObject(JSON.toJSONString(entry.getValue()), JSONObject.class);

                    // 订单不为零，且报酬不为零，才需要发通知
                    if (object.getLong("orderNum") > 0 && object.getDouble("reward") > 0) {

                        // 给区域服务商发送通知。
                        HashMap<String, Object> params = new HashMap<>();
//                        params.put("first", "【区域服务商" + object.getString("providerId") + "】恭喜您，活动已成" + groupType + "！您售出" + object.getLong("goodsNum") + "件商品，本次预计返利" + String.format("%.2f", allReward) + "元，其中" + String.format("%.2f", receivedReward) + "元已到账。");
                        params.put("first", "【区域服务商" + object.getString("providerId") + "】亲，本场活动已结束，活动累计总销量" + totalBuyQuantity + "件，已成" + groupType + "。您所在的区域产生" + object.getLong("orderNum") + "个订单，合计" + object.getLong("goodsNum") + "件商品，预计获得平台奖励" + String.format("%.2f", object.getDouble("reward")) + "元！");
                        params.put("keyword1", cycleId);// 订单编号(周期串)
                        params.put("keyword2", goods.getGoodsName());// 商品名称
                        params.put("remark", "月底统一结算并发放奖励！您所在区域发现不少潜在消费者，建议您发起商品销售，平台现有大量零食/美妆/洗护/母婴/百货等商品销售资源，佣金非常可观，欢迎前往选品！");
                        params.put("pagepath", "/pages/index/evaluationIndex");// （链接到【平台-生活】）
                        params.put("pageurl", "/pages/index/evaluationIndex");// （链接到【平台-生活】）
                        params.put("docUnid", "");
                        params.put("userId", supplierId);

                        JSONObject obj = sendMsg(params);
                        if (!"200".equals(obj.getString("code"))) {
                            log.error("给区域服务商（" + supplierId + "）发送通知失败！");
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }

    @Override
    public GroupBuyingStatisticsDto getPublishGroupDataByInItiatorId(String initiatorId) {
        if (StringUtils.isBlank(initiatorId)){
            return null;
        }
        return baseMapper.groupBuyingStatistics(initiatorId);
    }

    /** ********************* 以下调用其他系统的 ******************************* */

    /**
     * 获取token与authstr
     *
     * @param
     * @return
     */
    public JSONObject getToken(JSONObject object) {
        HttpResponse resp = HttpRequest.post(getOrderSysTokenUrl)
                .header("Content-Type", "application/json")
                .header("Accept", "application/json")
                .body(object.toJSONString())
                .execute();
        String result = resp.body();

        String token = JSON.parseObject(result).getString("data");
        String tmpStr = authSalt.charAt(2) + authSalt.charAt(8) + token + authSalt.charAt(10) + authSalt.charAt(5);
        object.put("token", token);
        object.put("authStr", SecureUtil.md5(tmpStr));
        return object;
    }

    /**
     * 积分系统：转入钱包
     *
     * @return
     */
    public String subPayToWallet(JSONObject object) {
        HttpResponse resp = HttpRequest.post(subPayToWallet)
                .header("Content-Type", "application/json")
                .header("Accept", "application/json")
                .body(object.toJSONString())
                .execute();
        return resp.body();
    }

    /**
     * 发送订单通知
     *
     * @param params 实例对象
     * @return
     */
    public JSONObject sendMsg(HashMap<String, Object> params) {
        params.put("appid", msgAppid);
        params.put("wf_num", msgWfNum);
        HttpResponse resp = HttpRequest.post(bpmUrl)
                .header("sysid", sysId)
                .header("syspwd", sysPwd)
                .header("userId", "admin")
                .form(params)
                .execute();
        return JSON.parseObject(resp.body());
    }

}
